/*
* otfeed_symbol_hist.cpp This example program illustrates the mechanism
* to be used for connecting to the otFeed servers and obtaining historical
* quotes of symbols from the servers.
*/
#include "otfeed_servers.h"
#include "SymbolHistClient.h"
#include <boost/program_options.hpp>
#include <vector>
#include <iostream>

namespace
{
    /**
     * Structure used to hold command line options specified by a user and/or
     * their default values. 
     */
    struct CommandLineOptions
    {
        /**
         * Default constructor. This constructor sets all options to their
         * default values. 
         */
        CommandLineOptions() ;

        /**
         * Convert the command line options into a SymbolHistClient DataRequest.
         */
        OtfeedExamples::SymbolHistClient::DataRequest
        GetDataRequest() const ;

        //! OpenTick account/user name.
        std::string mAccountName ;

        //! OpenTick account password.
        std::string mAccountPassword ;

        //! Code of the exchange corresponding to the requested symbol.
        std::string mExchange ;

        //! Symbol for which data is to be retrieved.
        std::string mSymbol ;

        //! Name of the output file.
        std::string mOutputFile ;

        //! Column separator to be used while writing data to the output file.
        std::string mColumnSeparator ;

        //! OpenTick server to be used for obtaining data.
        std::string mOpenTickServerName ;

        //! Port to be used while connecting to the OpenTick server.
        unsigned int mOpenTickServerPort ;

        //! Start date for the historical data to be retrieved.
        std::string mFromDate ;

        //! End date for the historical data.
        std::string mToDate ;

        //! Flag that is set to enable verbose messages.
        bool mIsVerbose ;

        //! Flag that is used to enable debugging.
        bool mIsDebugging ;
    } ;

    //! Display CommandLineOptions to an output stream.
    std::ostream&
    operator<<( std::ostream& outputStream,
                const CommandLineOptions& cmdLineOptions )
    {
        //
        // Display values of all parameters but never display the password.
        //
        outputStream << std::endl
                     << "Account/User Name  : " << cmdLineOptions.mAccountName << std::endl
                     << "Account Password   : " << "********" << std::endl
                     << "Exchange           : " << cmdLineOptions.mExchange << std::endl
                     << "Symbol             : " << cmdLineOptions.mSymbol << std::endl
                     << "Output File        : " << cmdLineOptions.mOutputFile << std::endl
                     << "Column Separator   : " << cmdLineOptions.mColumnSeparator << std::endl
                     << "Opentick server    : " << cmdLineOptions.mOpenTickServerName << std::endl
                     << "Server port        : " << cmdLineOptions.mOpenTickServerPort << std::endl
                     << "Start date         : " << cmdLineOptions.mFromDate << std::endl
                     << "End date           : " << cmdLineOptions.mToDate << std::endl
                     << "Verbose            : " << ( cmdLineOptions.mIsVerbose ? "yes" : "no" ) << std::endl
                     << "Debugging          : " << ( cmdLineOptions.mIsDebugging ? "yes" : "no" ) << std::endl ;
        return outputStream ;
    }

    /**
     * Parse the argument passed to this program and extract the command
     * line options specified by the user. Default values of unspecified
     * options are placed in CommandLineOptions.
     */
    CommandLineOptions
    get_command_line_options( int numOfArguments,
                              char** argumentList ) ;
}

int main( int argc,
          char** argv )
{
    CommandLineOptions cmd_line_options =
        get_command_line_options( argc, argv ) ;
    //
    if ( cmd_line_options.mIsVerbose )
    {
        std::clog << cmd_line_options ;
    }
    //
    // Create a data request for the SymbolHistClient based on the
    // command line parameters specified by the user.
    //
    using namespace OtfeedExamples ;
    SymbolHistClient::DataRequest data_request = 
        cmd_line_options.GetDataRequest() ;
    /*
     * Create an instance of SymbolHistClient and request historical data. 
     */
    SymbolHistClient hist_client ;
    hist_client.GetHistData( data_request ) ;
    return EXIT_SUCCESS ;
}

namespace
{
    CommandLineOptions::CommandLineOptions() :
            mColumnSeparator( "," ),
            mIsVerbose( false ),
            mIsDebugging( false )
    {
        /* empty */
        return ;
    }

    OtfeedExamples::SymbolHistClient::DataRequest
    CommandLineOptions::GetDataRequest() const 
    {
        using namespace OtfeedExamples ;
        SymbolHistClient::DataRequest data_request ;
        data_request.mOpenTickServer = this->mOpenTickServerName ;
        data_request.mOpenTickServerPort = this->mOpenTickServerPort ;
        data_request.mAccountLogin = this->mAccountName ;
        data_request.mAccountPassword = this->mAccountPassword ;
        data_request.mExchangeCode = this->mExchange ;
        data_request.mSymbol = this->mSymbol ;
        data_request.mFromDate = this->mFromDate ;
        data_request.mToDate = this->mToDate ;
        data_request.mIsVerbose = this->mIsVerbose ;
        data_request.mOutputFileName = this->mOutputFile ;
        return data_request ;
    }

    CommandLineOptions
    get_command_line_options( int numOfArguments,
                              char** argumentList )
    {
        /*
         * Use boost::program_options to parse the command line for options.
         */
        namespace po = boost::program_options ;
        //
        // Specify the list of allowed command line options.
        //
        CommandLineOptions options ;
        //
        // Parameters that are required on command line.
        //
        po::options_description required_options( "Required" ) ;
        required_options.add_options()
        ( "user,u",
          po::value<std::string>( &options.mAccountName ),
          "OpenTick account/user name" )
        ( "password,p",
          po::value<std::string>( &options.mAccountPassword ),
          "OpenTick account/user password" )
        ( "exchange,e",
          po::value<std::string>( &options.mExchange ),
          "Exchange code for which symbol list is to be obtained" )
        ( "symbol,s",
          po::value<std::string>( &options.mSymbol ),
          "Symbol code for which data is to be obtained" )
        ( "outfile,o",
          po::value<std::string>( &options.mOutputFile ),
          "Output file name" )
        ( "server,H",
          po::value<std::string>( &options.mOpenTickServerName ),
          "OpenTick server host name" )
        ( "port,P",
          po::value<unsigned int>( &options.mOpenTickServerPort ),
          "OpenTick server port" )
        ( "from,F",
          po::value<std::string>( &options.mFromDate ),
          "Start date for historical data" )
        ( "to,T",
          po::value<std::string>( &options.mToDate ),
          "End date for historical data" )
        ;
        //
        // Optional parameters.
        //
        po::options_description optional_parameters( "Optional" ) ;
        optional_parameters.add_options()
        ( "help,h",
          "Display help message and exit" )
        ( "verbose,v",
          "Display verbose messages" )
        ( "debug,d",
          "Display debugging information\nFor developers only" )
        ( "separator,S",
          po::value<std::string>( &options.mColumnSeparator ),
          "Column separtor in output file\n(default: ',')" )
        ;
        po::options_description allowed_options( "Allowed command line parameters" ) ;
        allowed_options.add( required_options ).add( optional_parameters ) ;
        //
        // Parse the command line and extract options from it.
        //
        po::variables_map parsed_options ;
        po::store( po::parse_command_line( numOfArguments,
                                           argumentList,
                                           allowed_options ),
                   parsed_options ) ;
        po::notify( parsed_options ) ;
        //
        // If the "help" option is specified then display the help message and
        // exit immediately.
        //
        if ( parsed_options.count( "help" ) )
        {
            std::cout << allowed_options << std::endl ;
            exit( EXIT_SUCCESS ) ;
        }
        //
        // Check if verbosity is to be enabled.
        //
        options.mIsVerbose = ( 0 != parsed_options.count( "verbose" ) ) ;
        //
        // Check if debugging is enabled.
        //
        options.mIsDebugging = ( 0 != parsed_options.count( "debug" ) ) ;
        /*
         * Check if all the required options have been specified.
         */
        if ( ! parsed_options.count( "exchange" ) )
        {
            std::cerr << "ERROR: No exchange code specified." << std::endl ;
            std::cerr << std::endl ;
            std::cerr << allowed_options << std::endl ;
            exit( EXIT_FAILURE ) ;
        }
        if ( ! parsed_options.count( "symbol" ) )
        {
            std::cerr << "ERROR: No symbol code specified." << std::endl ;
            std::cerr << std::endl ;
            std::cerr << allowed_options << std::endl ;
            exit( EXIT_FAILURE ) ;
        }
        if ( ! parsed_options.count( "outfile" ) )
        {
            std::cerr << "ERROR: No output file specified." << std::endl ;
            std::cerr << std::endl ;
            std::cerr << allowed_options << std::endl ;
            exit( EXIT_FAILURE ) ;
        }
        if ( ! parsed_options.count( "server" ) )
        {
            std::cerr << "ERROR: No server name specified." << std::endl ;
            std::cerr << std::endl ;
            std::cerr << allowed_options << std::endl ;
            exit( EXIT_FAILURE ) ;
        }
        if ( ! parsed_options.count( "port" ) )
        {
            std::cerr << "ERROR: No server port specified." << std::endl ;
            std::cerr << std::endl ;
            std::cerr << allowed_options << std::endl ;
            exit( EXIT_FAILURE ) ;
        }
        if ( ! parsed_options.count( "from" ) )
        {
            std::cerr << "ERROR: No start date specified." << std::endl ;
            std::cerr << std::endl ;
            std::cerr << allowed_options << std::endl ;
            exit( EXIT_FAILURE ) ;
        }
        if ( ! parsed_options.count( "to" ) )
        {
            std::cerr << "ERROR: No end date specified." << std::endl ;
            std::cerr << std::endl ;
            std::cerr << allowed_options << std::endl ;
            exit( EXIT_FAILURE ) ;
        }
        //
        // If the username is not specified then ask the user to type in the
        // user name otherwise store the specified user name.
        //
        if ( ! parsed_options.count( "user" ) )
        {
            std::cout << "Please, enter your login: " ;
            std::cin >> options.mAccountName ;
        }
        //
        // Similarly, if the password is specified then save it otherwise
        // ask the user for the password.
        //
        if ( ! parsed_options.count( "password" ) )
        {
            std::ostringstream password_prompt ;
            password_prompt << "Password for " << options.mAccountName << ": " ;
            //
            options.mAccountPassword = getpass( password_prompt.str().c_str() ) ;
        }
        //
        return options ;
    }
}
